package br.edu.ufcg.splab.techniques.reduction.strategies;

import java.text.DecimalFormat;
import java.util.List;

import br.edu.ufcg.splab.generator.TestCase;
import br.edu.ufcg.splab.generator.TestSuite;
import br.edu.ufcg.splab.techniques.reduction.functions.similarity.DistanceFunction;
import br.edu.ufcg.splab.util.MapFaultAndTestSuite;

public class SimilarityMatrix {
		
	/**
	 * Create a matrix where each element M[l, c] represents the similarity between the 
	 * paths in the line l and the column c.
	 * 
	 * @param matchedPaths matching paths
	 * @param function The distance function used to create the matrix
	 * @return matrix with the similarity.
	 */
	public double[][] createMatrix(TestSuite matchedPaths, DistanceFunction function) {
		double[][] matrix = new double[matchedPaths.size()][matchedPaths.size()];
		for (int line = 0; line < matrix.length; line++) {
			for (int column = line + 1; column < matrix.length; column++) {
				if(line == column){
					matrix[line][column] = 0.00;				
				}else{
					double num = function.getSimilarity(matchedPaths.get(line), matchedPaths.get(column));
					matrix[line][column] = num;
				}
			}
		}
		return matrix;
	}	
	
	/**
	 * Create a new matrix that is a copy of the previous matrix except for the line of the
	 * removed test case.
	 * @param matrix The matrix to be copied.
	 * @param lineToRemove The line to be excluded.
	 * @return The new matrix.
	 */
	public double[][] removedLineAndColumn(double[][] matrix, int lineToRemove) {
		double[][] newMatrix = new double[matrix.length - 1][matrix.length - 1];
		int newLine = 0; //variable used to control the lines of the new matrix. 
		for (int i = 0; i < matrix.length; i++) {
			int newColumn = 0; //variable used to control the lines of the new matrix.
			if(i != lineToRemove){
				for (int j = 0; j < matrix.length; j++) {
					if(j != lineToRemove){
						newMatrix[newLine][newColumn] = matrix[i][j];
						newColumn++;
					}
				}
				newLine++;
			}
		}
		matrix = newMatrix;
		return matrix;
	}
	
	protected final String TAB = "	";
	protected final String EOL = System.getProperty("line.separator");

	
	public String printMatrix(double[][] matrix) {
		String str = "";
	    DecimalFormat fmt = new DecimalFormat("##,##0.000");	  
		for (int line = 0; line < matrix.length; line++) {
		 	str = str + (line + 1) + TAB;
		 	for (int i = 0; i <= line; i++) {
		 		str = str + TAB;
			}
			for (int column = line + 1; column < matrix.length; column++) {
				str = str + (fmt.format(matrix[line][column])) + TAB;
			}
			str = str + EOL;			
		}
		return str;
	}
	
	public String printMatrix(double[][] matrix, List<Integer> tcs) {
		String str = "";
	    DecimalFormat fmt = new DecimalFormat("##,##0.000");	  
		for (int line = 0; line < matrix.length; line++) {
			str = str + (tcs.get(line)) + TAB;
		 	for (int i = 0; i <= line; i++) {
		 		str = str + TAB;
			}
			for (int column = line + 1; column < matrix.length; column++) {
				str = str + (fmt.format(matrix[line][column])) + TAB;
			}
			str = str + EOL;
			
		}
		return str;
	}
	
	public String printMatrix(double[][] matrix,  List<TestCase> tcs, MapFaultAndTestSuite mapTS_Faults) {
		System.out.println("tamanho matriz: " + matrix.length);
		String str = "";
	    DecimalFormat fmt = new DecimalFormat("##,##0.000");	  
		for (int line = 0; line < matrix.length; line++) {
			str = str + (mapTS_Faults.getValueTestCase(tcs.get(line))) + TAB;
		 	for (int i = 0; i <= line; i++) {
		 		str = str + TAB;
			}
			for (int column = line + 1; column < matrix.length; column++) {
				str = str + (fmt.format(matrix[line][column])) + TAB;
			}
			str = str + EOL;
		}
		return str;
	}

	/**
	 * Returns the maximum value existing in the matrix.
	 * @param matchedPaths matching paths
	 * @param function The distance function used to create the matrix
	 * @return The maximum value existing in the matrix.
	 */
	public double getMaximumValue(TestSuite matchedPaths, DistanceFunction function){
		double[][] matrix = createMatrix(matchedPaths, function) ;
		double maximumValue = Double.MIN_VALUE;
		for (int line = 0; line < matrix.length; line++) {
			for (int column = line + 1; column < matrix.length; column++) {
				if(matrix[line][column] > maximumValue){
					maximumValue = matrix[line][column];
				}
			}
		}
		return maximumValue;
	}
	
	public double getAverage(TestSuite matchedPaths, DistanceFunction function){
		double[][] matrix = createMatrix(matchedPaths, function) ;
		double values = 0.00;
		int cont = 0;
		for (int line = 0; line < matrix.length; line++) {
			for (int column = line + 1; column < matrix.length; column++) {
				values = values + matrix[line][column];
				cont++;				
			}
		}
		return (double) (values/cont);
	}
	
	
	/**
	 * Returns the minimum value existing in the matrix.
	 * @param matchedPaths matching paths
	 * @param function The distance function used to create the matrix
	 * @return The minimum value existing in the matrix.
	 */
	public double getMinimumValue(TestSuite matchedPaths, DistanceFunction function){
		double[][] matrix = createMatrix(matchedPaths, function) ;
		double minimumValue = Double.MAX_VALUE;
		for (int line = 0; line < matrix.length; line++) {
			for (int column = line + 1; column < matrix.length; column++) {
				if(matrix[line][column] < minimumValue){
					minimumValue = matrix[line][column];
				}
			}
		}
		return minimumValue;
	}
	
}
